package com.gjs.common.manager.sso;

import cn.hutool.core.map.MapUtil;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import cn.hutool.http.HttpUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.gjs.common.manager.response.RspEnum;
import com.gjs.common.pojo.dto.user.UserInfoDTO;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;

import java.io.IOException;
import java.util.HashMap;

/**
 * 客户端封装类
 *
 * @author JC_Ho
 */
@Data
@AllArgsConstructor
@NoArgsConstructor
@Slf4j
public class SsoClient {

    private String clientId;

    private String clientSecret;

    private String accessTokenUri;

    private String userAuthorizationUri;

    private String userInfoUri;

    private String redirectUri;

    private Integer timeout = 3000;

    public SsoClient(String clientId, String clientSecret, String accessTokenUri, String userAuthorizationUri, String userInfoUri, String redirectUri) {
        this.setClientId(clientId);
        this.setClientSecret(clientSecret);
        this.setAccessTokenUri(accessTokenUri);
        this.setUserAuthorizationUri(userAuthorizationUri);
        this.setUserInfoUri(userInfoUri);
        this.setRedirectUri(redirectUri);
    }

    public String buildAuthorizeUrl(String state, String... scope) {
        HashMap<String, Object> paramMap = new HashMap<>();
        paramMap.put("client_id", this.getClientId());
        paramMap.put("response_type", "code");
        paramMap.put("state", state);
        paramMap.put("scope", String.join(",", scope));
        return String.join("?", this.getUserAuthorizationUri(), HttpUtil.toParams(paramMap));
    }

    public AccessTokenRsp getAccessToken(String code, String scope) throws IOException, NullPointerException {
        HashMap<String, Object> paramMap = new HashMap<>();
        paramMap.put("grant_type", "authorization_code");
        paramMap.put("client_id", this.getClientId());
        paramMap.put("client_secret", this.getClientSecret());
        paramMap.put("redirect_uri", this.getRedirectUri());
        paramMap.put("code", code);
        paramMap.put("scope", scope);

        return this.getAccessToken(paramMap);
    }

    public AccessTokenRsp getAccessToken(String userAccount, String password, String scope) throws IOException, NullPointerException {
        HashMap<String, Object> paramMap = new HashMap<>();
        paramMap.put("grant_type", "password");
        paramMap.put("client_id", this.getClientId());
        paramMap.put("client_secret", this.getClientSecret());
        paramMap.put("scope", String.join(",", scope));
        paramMap.put("username", userAccount);
        paramMap.put("password", password);

        return this.getAccessToken(paramMap);
    }

    public AccessTokenRsp getAccessToken(HashMap<String, Object> paramMap) throws IOException, NullPointerException {
        HttpResponse response = HttpRequest.post(this.getAccessTokenUri()).form(paramMap).timeout(this.getTimeout()).execute();
        if (!response.isOk()) {
            throw new IOException("请求access_token失败");
        }

        AccessTokenRsp accessTokenRsp = JSONUtil.toBean(response.body(), AccessTokenRsp.class);
        if (accessTokenRsp.getAccessToken() == null) {
            throw new NullPointerException("缺乏有效access_token");
        }

        return accessTokenRsp;
    }

    public UserInfoDTO getUserInfo(String accessToken) throws IOException {
        String urlWithAccessToken = String.join("?", this.getUserInfoUri(), HttpUtil.toParams(MapUtil.of("access_token", accessToken)));
        HttpResponse response = HttpRequest
                .get(urlWithAccessToken)
                .timeout(this.getTimeout())
                .execute();
        if (response.isOk()) {
            JSONObject jsonObject = JSONUtil.parseObj(response.body());
            if (jsonObject.getStr("code") == null || !jsonObject.getStr("code").equals(RspEnum.SUCCESS.getResultCode().toString())) {
                throw new IOException("请求user_info失败");
            }
            return JSONUtil.toBean(jsonObject.getStr("data"), UserInfoDTO.class);
        } else {
            throw new IOException("请求user_info失败");
        }
    }

}
